﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Seagull.Configuration;
using Seagull.IDAL.Stat;
using Seagull.Model;
using Seagull.Model.Stat;
using Steema.TeeChart;

namespace Seagull.Controls
{
    public partial class StatChart : UserControl
    {
        private readonly Dictionary<DateTime, Statistic[]> _frameStatisticCache =
            new Dictionary<DateTime, Statistic[]>();

        private readonly StatisticsViewer _statisticsViewer = new StatisticsViewer();
        private ChannelStatistics _channelStatistics;

        private DateTime _hourViewStartTime;
        private ViewKind _viewKind = ViewKind.Year;

        private readonly byte _minDataValue = Config.Instance.DataReadConfig.MinDataValue;
        private readonly byte _maxDataValue = Config.Instance.DataReadConfig.MaxDataValue;

        private string _text;

        public StatChart()
        {
            InitializeComponent();
            _chart.Panning.Allow = ScrollModes.None;
        }

        /// <summary>
        /// 设置图表标题
        /// </summary>
        /// <returns>The text associated with this control.</returns>
        public override string Text
        {
            get { return _text; }
            set
            {
                _text = value;
                ResetTitle();
            }
        }


        /// <summary>
        /// 读取/设置图表的统计数据源。
        /// </summary>
        /// <value>
        /// The statistic source.
        /// </value>
        public IStatisticSource StatisticSource { get; set; }

        private ViewKind CurrentViewKind
        {
            get { return _viewKind; }
            set
            {
                _viewKind = value;
                ResetTitle();
            }
        }

        #region Switch Views

        private void GotoYearView()
        {
            _channelStatistics = _channelStatistics ??
                StatisticSource.GetChannelStatisticsAfter(DateTime.MinValue);
            ShowStatistics(_channelStatistics.HourValues);
            _chart.Axes.Bottom.Labels.DateTimeFormat = "yyyy/MM/dd";
            _chart.Axes.Bottom.Increment = Utils.GetDateTimeStep(DateTimeSteps.OneHour);
            CurrentViewKind = ViewKind.Year;
        }

        private void GotoHourView(DateTime hour)
        {
            _hourViewStartTime = hour;
            var frameStatisticsOfHour = GetFrameStatisticsOfHour(hour);
            _statisticsViewer.ZoomInterval = TimeSpan.FromSeconds(1);
            _statisticsViewer.StartTime = hour;
            _statisticsViewer.FrameStatistics = frameStatisticsOfHour;
            ShowStatistics(_statisticsViewer.ZoomedStatistics);
            _chart.Axes.Bottom.Labels.DateTimeFormat = "HH:mm:ss";
            _chart.Axes.Bottom.Increment = Utils.GetDateTimeStep(DateTimeSteps.OneMinute);
            CurrentViewKind = ViewKind.Hour;
        }

        private void GotoSecondView(DateTime hourStartTime, DateTime second)
        {
            var frameStatisticsOfHour = GetFrameStatisticsOfHour(hourStartTime);
            _statisticsViewer.FrameStatistics = frameStatisticsOfHour;
            _statisticsViewer.StartTime = hourStartTime;
            ShowStatistics(_statisticsViewer.GetStatisticsWithin(second, TimeSpan.FromSeconds(1)));
            _chart.Axes.Bottom.Labels.DateTimeFormat = "HH:mm:ss.fff";
            // BUG: not working
            _chart.Axes.Bottom.Increment = Utils.GetDateTimeStep(DateTimeSteps.OneMillisecond) * 20;
            CurrentViewKind = ViewKind.Second;
        }

        #endregion


        #region Event Handlers

        private void OnChartDoubleClick(object sender, EventArgs e)
        {
            var xIndex = _avgNearestPoint.Point;
            if(xIndex >= 0)
            {
                switch(CurrentViewKind)
                {
                    case ViewKind.Year:
                        GotoHourView(_channelStatistics.StartTime.AddHours(xIndex));
                        break;
                    case ViewKind.Hour:
                        GotoSecondView(_hourViewStartTime, _hourViewStartTime.AddSeconds(xIndex));
                        break;
                    case ViewKind.Second:
                        // Nothing happens
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        private void OnChartUndoneZoom(object sender, EventArgs e)
        {
            GotoYearView();
        }

        private void OnAvgNearestPointChange(object sender, EventArgs e)
        {
            var xValue = _avgNearestPoint.Point;
            if(xValue < 0)
            {
                return;
            }
            string xLabel;
            switch(CurrentViewKind)
            {
                case ViewKind.Year:
                    xLabel = _channelStatistics.StartTime.AddHours(xValue).ToString("yyyy/MM/dd HH:mm");
                    break;
                case ViewKind.Hour:
                    xLabel = _hourViewStartTime.AddSeconds(xValue).ToString("HH:mm:ss");
                    break;
                case ViewKind.Second:
                    xLabel = (xValue * FrameSet.DefaultNormalFrameSpaceMilliseconds) + "ms";
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            var yLabel = string.Format("最大值{0:0}% 平均值{1:0}%",
                _maxSeries.YValues[xValue] * 100, _avgSeries.YValues[xValue] * 100);
            _annotation.Text = string.Format("{0} {1}", xLabel, yLabel);
        }

        private void OnChartMouseMove(object sender, MouseEventArgs e)
        {
            _avgNearestPoint.Active = true;
            _annotation.Active = !string.IsNullOrWhiteSpace(_annotation.Text) && _avgNearestPoint.Point != -1;
            if(_annotation.Active)
            {
                var location = e.Location + new Size(16, 16);
                _annotation.Left = location.X;
                _annotation.Top = location.Y;
            }
        }

        private void OnChartMouseLeave(object sender, EventArgs e)
        {
            _annotation.Active = false;
            _avgNearestPoint.Active = false;
        }

        private void OnChartMouseClick(object sender, MouseEventArgs e)
        {
        }

        #endregion


        #region Helpers

        private void ResetTitle()
        {
            _chart.Header.Lines = new string[]
            {
                _avgSeries.XValues.Count > 0
                    ?string.Format("{0}    {1} - {2}", _text, 
                        Utils.DateTime(_avgSeries.XValues.Minimum).ToString("yyyy/MM/dd HH:mm:ss"),
                        Utils.DateTime(_avgSeries.XValues.Maximum).ToString("yyyy/MM/dd HH:mm:ss"))
                    :string.Format("{0} 无数据", _text)
            };
        }

        public void Reset()
        {
            ResetChart();
            // 先显示所有的小时数据
            GotoYearView();
        }

        private void ShowStatistics(IEnumerable<TimeStatistic> timeStatistics)
        {
            _maxSeries.Clear();
            _avgSeries.Clear();
            foreach(var hourStatistic in timeStatistics)
            {
                _maxSeries.Add(hourStatistic.Time, NormalizeDataValue(hourStatistic.Statistic.Max));
                _avgSeries.Add(hourStatistic.Time, NormalizeDataValue(hourStatistic.Statistic.Avg));
            }
        }

        private double NormalizeDataValue(byte value)
        {
            if(value == 0)
            {
                return 0;
            }
            return (value - _minDataValue) * 1.0 / (_maxDataValue - _minDataValue);
        }

        private void ResetChart()
        {
            _maxSeries.Clear();
            _avgSeries.Clear();
        }

        private Statistic[] GetFrameStatisticsOfHour(DateTime hour)
        {
            Statistic[] frameStatisticsOfHour;
            if(!_frameStatisticCache.TryGetValue(hour, out frameStatisticsOfHour))
            {
                frameStatisticsOfHour = StatisticSource.GetFrameStatisticsOfHour(hour);
                _frameStatisticCache.Add(hour, frameStatisticsOfHour);
            }
            return frameStatisticsOfHour;
        }

        private string GetViewKindText(ViewKind viewKind)
        {
            switch(viewKind)
            {
                case ViewKind.Year:
                    return "所有历史";
                case ViewKind.Hour:
                    return "小时视图";
                case ViewKind.Second:
                    return "秒视图";
                default:
                    throw new ArgumentOutOfRangeException("viewKind");
            }
        }

        #endregion


        #region Nested type: ViewKind

        private enum ViewKind
        {
            Year,
            Hour,
            Second
        }

        #endregion

    }
}